package top.cluski.feieprint.service;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.dtflys.forest.Forest;
import com.dtflys.forest.http.ForestResponse;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.update.UpdateChain;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import top.cluski.feieprint.api.teable.TeableApi;
import top.cluski.feieprint.api.teable.entity.Filter;
import top.cluski.feieprint.api.teable.entity.FilterItem;
import top.cluski.feieprint.api.teable.entity.RecordAddEntity;
import top.cluski.feieprint.api.teable.entity.RecordEntity;
import top.cluski.feieprint.api.youzan.YouzanApi;
import top.cluski.feieprint.api.youzan.entity.TradeOrderRespEntity;
import top.cluski.feieprint.domain.dto.YouzanMatchOrderDTO;
import top.cluski.feieprint.domain.dto.YouzanShipDTO;
import top.cluski.feieprint.entity.YouzanDelayShipEntity;
import top.cluski.feieprint.entity.YouzanErrCallbackEntity;
import top.cluski.feieprint.mapper.YouzanDelayShipMapper;
import top.cluski.feieprint.mapper.YouzanErrCallbackMapper;

import java.io.File;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
import static top.cluski.feieprint.api.teable.entity.Filter.Conjunction.and;
import static top.cluski.feieprint.api.teable.entity.Filter.Operator.is;

@Slf4j
@Service
public class YouzanService {
    @Resource
    YouzanApi youzanApi;
    @Resource
    TeableApi teableApi;

    @Resource
    YouzanErrCallbackMapper youzanErrCallbackMapper;
    @Resource
    YouzanDelayShipMapper youzanDelayShipMapper;

    public void matchOrder(YouzanMatchOrderDTO youzanMatchOrderDTO) {
        boolean flg = false;
        List<TradeOrderRespEntity.FullOrderInfo> matchOrderInfos = new ArrayList<>();
        TradeOrderRespEntity.DataResponse tradeOrders = youzanApi.getTradeOrder(youzanMatchOrderDTO.getMobile());

        for (TradeOrderRespEntity.FullOrderInfoList fullOrderInfoList : tradeOrders.getFull_order_info_list()) {
            String receiverTel = youzanApi.decrypt(fullOrderInfoList.getFull_order_info().getAddress_info().getReceiver_tel());

            if ("自取".equals(youzanMatchOrderDTO.getDeliveryType())) {
                if (StrUtil.isNotBlank(fullOrderInfoList.getFull_order_info().getAddress_info().getSelf_fetch_info())
                        && StrUtil.equals(receiverTel, youzanMatchOrderDTO.getMobile())
                        && StrUtil.isBlank(fullOrderInfoList.getFull_order_info().getAddress_info().getDelivery_start_time())) {
                    flg = true;
                    matchOrderInfos.add(fullOrderInfoList.getFull_order_info());
                }
            } else {
                String deliveryDate = LocalDateTime.parse(fullOrderInfoList.getFull_order_info().getAddress_info().getDelivery_start_time(), DatePattern.NORM_DATETIME_FORMATTER)
                        .format(DatePattern.NORM_DATE_FORMATTER);
                if (StrUtil.equals(receiverTel, youzanMatchOrderDTO.getMobile())
                        && isAddressMatch(youzanMatchOrderDTO.getAddress(), fullOrderInfoList.getFull_order_info().getAddress_info())
                        && StrUtil.equals(youzanMatchOrderDTO.getDeliveryDate(), deliveryDate)) {
                    flg = true;
                    matchOrderInfos.add(fullOrderInfoList.getFull_order_info());
                }
            }
        }

        if (!flg) {
            log.error("未匹配到订单 dto={}", youzanMatchOrderDTO);
            throw new RuntimeException("未匹配到订单");
        } else {
            log.info("成功匹配到订单 orders={}", matchOrderInfos);
            for (TradeOrderRespEntity.FullOrderInfo matchOrderInfo : matchOrderInfos) {
                if (matchOrderInfo.getOrder_info().getStatus() != TradeOrderRespEntity.TradeStatus.WAIT_SELLER_SEND_GOODS) {
                    log.warn("该订单已发货，跳过发货 order={}", matchOrderInfo);
                } else {
                    youzanApi.confirmShip(matchOrderInfo.getOrder_info().getTid());
                }
            }
        }
    }

    private boolean isAddressMatch(String targetAddress, TradeOrderRespEntity.AddressInfo addressInfo) {
        String deliveryAddress = youzanApi.decrypt(addressInfo.getDelivery_address());

        return StrUtil.contains(deliveryAddress, targetAddress) ||
                StrUtil.equals(targetAddress, addressInfo.getDelivery_province() + "-" + addressInfo.getDelivery_city() + "-" + addressInfo.getDelivery_district() + "-" + deliveryAddress);

    }

    @SneakyThrows
    @Async("myAsync")
    public void callback(JSONObject jsonObject, boolean fromScheduler) {
        try {
            switch (jsonObject.getString("type")) {
                case "trade_TradeBuyerPay" -> callbackSync(jsonObject);
                case "trade_refund_RefundSellerAgree" -> {
                    String orderId = jsonObject.getString("id");
                    RecordEntity recordEntity = teableApi.filterByYouzanOrderId(orderId);
                    if (recordEntity == null) {
                        log.warn("用户退款成功，但是在teable中没有找到记录 orderId={}", orderId);
                    } else {
                        delOrderRecord(recordEntity);
                    }

                }
                default -> {
                }
            }
        } catch (Exception ex) {
            if (fromScheduler) {
                throw ex;
            } else {
                log.error("有赞回调处理出错了!!", ex);
                youzanErrCallbackMapper.insert(YouzanErrCallbackEntity.create()
                        .setText(jsonObject.toJSONString())
                        .setCreateTime(LocalDateTime.now()));
            }
        }
    }

    private void delOrderRecord(RecordEntity recordEntity) {
        String id = recordEntity.getId();
        teableApi.delRecord(id);
    }

    public void callbackSync(JSONObject jsonObject) {
        String orderId = jsonObject.getString("id");
        RecordEntity recordEntity = teableApi.filterByYouzanOrderId(orderId);

        String msg = jsonObject.getString("msg");
        String decodedString = URLDecoder.decode(msg, StandardCharsets.UTF_8);

        TradeOrderRespEntity.FullOrderInfo fullOrderInfo = JSONObject.parseObject(decodedString).getJSONObject("full_order_info").toJavaObject(TradeOrderRespEntity.FullOrderInfo.class);

        TradeOrderRespEntity.FullOrderInfo orderDetail = youzanApi.getOrderDetail(fullOrderInfo.getOrder_info().getTid());
        log.info("获取到有赞订单详细信息 orderDetail={}", JSONObject.toJSONString(orderDetail));
        List<RecordAddEntity> records = orderDetail.getOrders().stream()
//                .filter(order -> !"P231019078725307".equals(order.getSku_barcode()))
                .flatMap(order -> RecordAddEntity.convert(orderDetail, order, true).stream())
                .toList();


        if (records.stream().filter(item -> item.getPrice() != null).toList().isEmpty()) {
            // 全是配件的订单
            List<RecordAddEntity> records2 = new ArrayList<>();
            records2.addAll(RecordAddEntity.convert(orderDetail, orderDetail.getOrders().get(0), false));
            records2.addAll(records);
            records = records2;
        }
        String accessory = records.stream()
                .filter(item -> item.getPrice() == null)
                .collect(Collectors.toMap(RecordAddEntity::getStyle, RecordAddEntity::getRemark, (s, s2) -> s + "+" + s2))
                .entrySet()
                .stream()
                .map(item -> item.getKey() + "[" + item.getValue() + "]")
                .collect(Collectors.joining("\uD83D\uDD36"));


        if (StrUtil.isNotBlank(accessory)) {
            records.forEach(record -> {
                record.setRemark(
                        record.getRemark() + "\n蛋糕配件：" + accessory
                );
            });
        }

        records = records.stream().filter(record -> record.getPrice() != null).toList();

        List<String> picUrls = records.stream().map(RecordAddEntity::getPicUrl).toList();

        List<String> recordIdList = new ArrayList<>();
        if (recordEntity == null) {
            recordIdList = teableApi.addRecords(records);
        } else {
            String id = recordEntity.getId();
            recordIdList.add(id);
        }
        uploadAttachment(recordIdList, picUrls);
    }

    private void uploadAttachment(List<String> recordIds, List<String> picUrls) {
        for (int i = 0; i < recordIds.size(); i++) {
            String recordId = recordIds.get(i);
            String picUrl = picUrls.get(i);
            File srcFile = null;
            File targetFile = null;
            try {
                ForestResponse forestResponse = Forest.get(picUrl)
                        .setDownloadFile(true)
                        .executeAsResponse();
                srcFile = FileUtil.createTempFile();
                FileUtil.writeBytes(forestResponse.getByteArray(), srcFile);

                String tmpDirPath = FileUtil.getTmpDirPath();
                targetFile = FileUtil.newFile(tmpDirPath + File.separator + UUID.fastUUID().toString(true) + ".jpeg");
                ImgUtil.scale(srcFile, targetFile, 0.5f);
                teableApi.uploadAttachment(recordId, targetFile);
            } catch (Exception e) {
                log.error("scale图片上传图片的时候失败了 recordId={}", recordId, e);
            } finally {
                if (targetFile != null) {
                    FileUtil.del(targetFile);
                }
                if (srcFile != null) {
                    FileUtil.del(srcFile);
                }
            }
        }
    }

    public void confirmShip(YouzanShipDTO youzanShipDTO) {
        log.info("有赞确认发货 orderId={}", youzanShipDTO);
        // 查询一下预计送达时间
        TradeOrderRespEntity.FullOrderInfo orderDetail = youzanApi.getOrderDetail(youzanShipDTO.getOrderId());
        List<RecordEntity> teableRecords = teableApi.getRecords(Filter.create()
                .setConjunction(and)
                .setFilterSet(Set.of(FilterItem.create()
                        .setFieldId("有赞订单号")
                        .setOperator(is)
                        .setValue(youzanShipDTO.getOrderId()))));
        RecordEntity teableRecord = null;
        if (!teableRecords.isEmpty()) {
            teableRecord = teableRecords.get(0);
        }
        if (StrUtil.isEmpty(orderDetail.getAddress_info().getDelivery_start_time())) {
            // 自取订单
            log.info("自取订单，直接发货");
            youzanApi.confirmShip(youzanShipDTO.getOrderId());
            return;
        }
        LocalDateTime deliveryStartTime = LocalDateTime.parse(orderDetail.getAddress_info().getDelivery_start_time(), NORM_DATETIME_FORMATTER);
        LocalTime songChuShiJian;
        if (teableRecord != null) {
            songChuShiJian = LocalTime.parse(teableRecord.getFields().getString("送出时间"), DateTimeFormatter.ofPattern("HH:mm"));
        } else {
            songChuShiJian = LocalTime.now().minusMinutes(1);
        }
        if (LocalTime.now().isBefore(songChuShiJian)) {
            // 否则入库，使用定时任务查询发货
            log.warn("不在发货时间内，先入库");
            youzanDelayShipMapper.insert(YouzanDelayShipEntity.create()
                    .setYouzanOrderId(youzanShipDTO.getOrderId())
                    .setShipTime(LocalDateTime.of(LocalDate.now(), songChuShiJian)));
        } else {
            // 如果在范围内发货
            log.info("在发货时间内，直接发货");
            youzanApi.confirmShip(youzanShipDTO.getOrderId());
        }
    }


    public void confirmShipV2(YouzanShipDTO youzanShipDTO) {
        // TODO: 获取数据库中订单信息
        log.info("有赞确认发货 orderId={}", youzanShipDTO);
        this.confirmShip(youzanShipDTO);
    }

    @Scheduled(cron = "0 */5 * * * *")
    public void failCallbackRetry() {
        List<YouzanErrCallbackEntity> youzanErrCallbackEntities = youzanErrCallbackMapper.selectAll();
        for (YouzanErrCallbackEntity youzanErrCallbackEntity : youzanErrCallbackEntities) {
            JSONObject jsonObject = JSONObject.parseObject(youzanErrCallbackEntity.getText());
            String youzanOrderId = jsonObject.getString("id");
            RecordEntity recordEntity = teableApi.filterByYouzanOrderId(youzanOrderId);
            if (recordEntity != null) {
                log.info("【有赞回调异常处理定时任务】该记录已存在，pass record={}", recordEntity);
                UpdateChain.of(YouzanErrCallbackEntity.class)
                        .eq(YouzanErrCallbackEntity::getId, youzanErrCallbackEntity.getId())
                        .remove();
                continue;
            }

            log.info("【有赞回调异常处理定时任务】开始处理 orderId={}", youzanOrderId);
            this.callback(jsonObject, true);
            UpdateChain.of(YouzanErrCallbackEntity.class)
                    .eq(YouzanErrCallbackEntity::getId, youzanErrCallbackEntity.getId())
                    .remove();
        }
    }

    @Scheduled(cron = "0 */2 * * * *")
    public void confirmDelayShip() {
        List<YouzanDelayShipEntity> youzanDelayShipEntities = youzanDelayShipMapper.selectListByQuery(new QueryWrapper()
                .ge(YouzanDelayShipEntity::getShipTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
                .le(YouzanDelayShipEntity::getShipTime, LocalDateTime.now())
                .isNull(YouzanDelayShipEntity::getUpdateTime));
        log.info("延迟发货定时任务 entities={}", youzanDelayShipEntities);
        for (YouzanDelayShipEntity youzanDelayShipEntity : youzanDelayShipEntities) {
            String youzanOrderId = youzanDelayShipEntity.getYouzanOrderId();
            log.info("有赞延迟发货 orderId={}", youzanOrderId);
            try {
                youzanApi.confirmShip(youzanOrderId);
                youzanDelayShipMapper.update(YouzanDelayShipEntity.create()
                        .setId(youzanDelayShipEntity.getId())
                        .setUpdateTime(LocalDateTime.now()));
            } catch (Exception e) {
                log.error("", e);
            }
        }
    }
}
